{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Real-time Visualization Widget\n",
    "\n",
    "\n",
    "REBOUND comes with built-in real-time 3D visualizations. This feature can be used without python. This notebookbook describes how you can access real time visualizations directly from a Jupyter notebooks. Under the hood, the code that is running is the same as the one providing OpenGL visualizations. \n",
    "\n",
    "Using the real-time visualization widget makes setting up a simulation and debugging it very interactive and intuitive. You immediately see if the particles are roughly in the place where you want them, doing roughly what you expect them to do.\n",
    "\n",
    "For this widget to work, you will need a browser that supports WebAssembly and WebGL. All modern browsers should have those features enabled by default.\n",
    "\n",
    "Let us start this demo by setting up an empty simulation and calling the `widget()` function on the simulation object. This will create a new widget and attach it to the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"400\"\n",
       "            height=\"400\"\n",
       "            src=\"http://localhost:1234\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x102c9b5b0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import rebound\n",
    "sim = rebound.Simulation()\n",
    "sim.widget(size=(400,400))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next up, lets add some particles to the simulation. The widget updates automatically when a particle gets added or removed. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim.add(m=1) # add a star"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(10):\n",
    "    sim.add(m=1e-3,a=0.4+0.1*i,inc=0.03*i,omega=5.*i) # Jupiter mass planets on close orbits\n",
    "sim.move_to_com() # Move to the center of mass frame"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Drag the widget with your mouse or touchpad to look at the simulation from different angles. Keep the shift key pressed while you drag to zoom in or out. Try pressing the \"w\" button. This will toggle orbits on and off.\n",
    "\n",
    "Next, we will try to integrate the orbits forward in time. Because the planets are very massive and on close to each other, the system will go unstable very quickly. By default, REBOUND is using the IAS15 integrator which can resolve close encounter. During each close encounter the instantaneous orbits of the planets show in the widget will change rapidly.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim.integrate(500)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The widget will remain open until you delete the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "del sim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are some important things to understand regarding how these widgets work. We'll go over those next.\n",
    "\n",
    "### REBOUND is a web server!\n",
    "REBOUND includes its own web server. The widget connects to this web server to get the visualization code (a version of REBOUND compiled to WebAssembly) and the simulation data itself (in the form of Simulationarchive binary data). \n",
    "By default, the port the web server uses is 1234. You can start this webserver manually (without the widget) by running"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim = rebound.Simulation()\n",
    "sim.start_server()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can then connect to the REBOUND webserver by opening a new browser window at http://localhost:1234 or http://127.0.0.1:1234. If you want to stop the server, but not delete the simulation, you can run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim.stop_server()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Multiple simulations\n",
    "\n",
    "You can visualize multiple simulations at the same time. Each simulation needs to use have its port. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"200\"\n",
       "            height=\"200\"\n",
       "            src=\"http://localhost:1235\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x102d401f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sim1 = rebound.Simulation()\n",
    "sim1.start_server(port=1234)\n",
    "sim2 = rebound.Simulation()\n",
    "sim2.widget(port=1235, size=(200,200))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simulation 1 is shown in the widget above. You can view simulation 2 by going to http://localhost:1235 or by opening another widget:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"200\"\n",
       "            height=\"200\"\n",
       "            src=\"http://localhost:1235\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x104299f70>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sim2.widget(size=(200,200))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "del sim1, sim2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing simulations on a remote server\n",
    "\n",
    "Sometimes you might run a simulation on a remote server or computing cluster. When connecting to the remote server using ssh, you can forward the visualization ports the same way you would forward the port required for jupyter notebooks (8888 by default). REBOUND uses port 1234 by default, so you might want to enable port forwarding using \n",
    "\n",
    "```bash\n",
    "ssh username@remotecomputer -L 1234:localhost:1234\n",
    "```\n",
    "\n",
    "You can then connect to the visualization as usual, either using the widget you get with `sim.widget()` or by pointing your browser to http://localhost:1234."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Security and resource considerations\n",
    "\n",
    "The REBOUND web server provides a quick and easy way to visualize simulations. It is not intended to be exposed to the public internet because a malicious person might be able to gain access to your computer. \n",
    "\n",
    "The visualization uses a considerable amount of CPU resources. You might want to disable it if you no longer use it. \n",
    "\n",
    "Depending on your simulation (e.g. for simulations with a large number of particles), the communication between the REBOUND web server and your browser might use a lot of bandwidth. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
